package com.whfc.fim.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whfc.common.enums.HandleFlag;
import com.whfc.common.exception.BizException;
import com.whfc.common.result.PageData;
import com.whfc.common.result.R;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.PageUtil;
import com.whfc.fim.dao.FimAlgResultImgMapper;
import com.whfc.fim.dao.FimAlgResultMapper;
import com.whfc.fim.dao.FimAlgResultPersonMapper;
import com.whfc.fim.dao.FimWarnRuleMapper;
import com.whfc.fim.dto.*;
import com.whfc.fim.entity.FimAlgResult;
import com.whfc.fim.param.FimAlgResultDelParam;
import com.whfc.fim.param.FimResultBatchHandleParam;
import com.whfc.fim.param.FimResultEditParam;
import com.whfc.fim.param.FimSearchParam;
import com.whfc.fim.service.FimResultService;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: qzexing
 * @version: 1.0
 * @date: 2020/3/17 21:28
 */
@DubboService(interfaceClass = FimResultService.class, version = "1.0.0")
public class FimResultServiceImpl implements FimResultService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private FimAlgResultMapper fimAlgResultMapper;

    @Autowired
    private FimAlgResultImgMapper fimAlgResultImgMapper;

    @Autowired
    private FimWarnRuleMapper fimWarnRuleMapper;

    @Autowired
    private FimAlgResultPersonMapper fimAlgResultPersonMapper;

    private static final String HOUR_TIME_FORMAT = "HH:00";
    private static final String HOUR_TIME_SQL_FORMAT = "%H:00";
    private static final String DAY_TIME_FORMAT = "MM-dd";
    private static final String DAY_TIME_SQL_FORMAT = "%m-%d";


    @Override
    public PageData<FimResultDTO> list(Integer pageNum, Integer pageSize, Integer deptId, Integer handleStatus, Integer algCheckType, Date startTime, Date endTime) {

        PageHelper.startPage(pageNum, pageSize);
        List<FimResultDTO> list = fimAlgResultMapper.selectFimResultList(deptId, handleStatus, algCheckType, startTime, endTime);
        PageHelper.clearPage();

        for (FimResultDTO fimResultDTO : list) {
            Integer resultId = fimResultDTO.getResultId();
            List<String> personNameList = fimAlgResultPersonMapper.selectPersonNameByResultId(resultId, null);
            fimResultDTO.setPersonNameList(personNameList);

            List<String> imgList = fimAlgResultImgMapper.selectImgUrlByResultId(resultId);
            fimResultDTO.setResultImgUrlList(imgList);
        }
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public PageData<FimResultDTO> list(Integer pageNum, Integer pageSize, Integer deptId, Integer algCheckType, Date date) {
        PageHelper.startPage(pageNum, pageSize);
        List<FimResultDTO> list = fimAlgResultMapper.selectResultList(deptId, algCheckType, date);
        PageHelper.clearPage();
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public void del(FimAlgResultDelParam param) throws BizException {
        logger.info("批量删除智能监控报警记录服务,param:{}", param.toString());
        fimAlgResultMapper.deleteLogic(param.getResultIdList());
    }

    @Override
    public FimResultDTO detail(Integer resultId) {
        FimResultDTO result = fimAlgResultMapper.selectResultDTO(resultId);
        if (result == null) {
            return null;
        }
        List<String> personNameList = fimAlgResultPersonMapper.selectPersonNameByResultId(result.getResultId(), null);
        result.setPersonNameList(personNameList);
        //查找报警图片
        List<String> imgUrls = fimAlgResultImgMapper.selectImgUrlByResultId(resultId);
        result.setResultImgUrlList(imgUrls);
        return result;
    }

    @Override
    public void handle(FimResultEditParam editParam) {
        Integer resultId = editParam.getResultId();
        FimAlgResult result = fimAlgResultMapper.selectByPrimaryKey(resultId);
        if (result == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "该识别记录不存在.");
        }
        result.setHandleStatus(1);
        result.setHandleResult(editParam.getHandleResult());
        result.setHandleRemark(editParam.getHandleRemark());
        result.setHandleTime(new Date());
        result.setHandleUserId(editParam.getHandleUserId());
        result.setHandleUserName(editParam.getHandleUserName());
        fimAlgResultMapper.updateByPrimaryKeySelective(result);
    }

    @Override
    public void batchHandle(FimResultBatchHandleParam param) throws BizException {
        List<Integer> resultIdList = param.getResultIdList();
        String handleResult = param.getHandleResult();
        String handleRemark = param.getHandleRemark();
        Integer userId = param.getUserId();
        String userName = param.getUserName();
        fimAlgResultMapper.batchUpdateState(resultIdList, handleResult, handleRemark, userId, userName);
    }

    @Override
    public FimResultAnalysisDTO analysis(Integer deptId, Date startTime, Date endTime) {
        //根据天统计
        FimResultAnalysisDTO analysisDTO = new FimResultAnalysisDTO();
        startTime = DateUtil.getDateBegin(startTime);
        endTime = DateUtil.getDateEnd(endTime);
        //报警总数
        Integer total = fimAlgResultMapper.countResultTotal(Arrays.asList(deptId), startTime, endTime);
        analysisDTO.setTotal(total);
        //统计每种报警类型的报警总数
        List<FimAlgCheckTypeCountDTO> algCheckTypeCountList = fimAlgResultMapper.selectTagAmount(deptId, startTime, endTime);
        analysisDTO.setAlgCheckTypeCountList(algCheckTypeCountList);
        //统计报警频次
        List<FimResultRateContDTO> resultRateCountList = fimAlgResultMapper
                .selectTagFrequency(deptId, startTime, endTime, "%m/%d");
        analysisDTO.setResultRateCountList(resultRateCountList);
        return analysisDTO;
    }

    @Override
    public FimResultAnalysisDTO analysis(Integer deptId, Date date) {
        //根据天统计
        FimResultAnalysisDTO analysisDTO = new FimResultAnalysisDTO();
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        //报警总数
        Integer total = fimAlgResultMapper.countResultTotal(Arrays.asList(deptId), startTime, endTime);
        analysisDTO.setTotal(total);
        //统计每种报警类型的报警总数
        List<FimAlgCheckTypeCountDTO> algCheckTypeCountList = fimAlgResultMapper.selectTagAmount(deptId, startTime, endTime);
        analysisDTO.setAlgCheckTypeCountList(algCheckTypeCountList);
        //统计报警频次
        List<FimResultRateContDTO> resultRateCountList = fimAlgResultMapper
                .selectTagFrequency(deptId, startTime, endTime, "%H" + ":00");
        analysisDTO.setResultRateCountList(resultRateCountList);
        return analysisDTO;
    }

    @Override
    public FimResultAnalysisDTO analysisRank(Integer deptId, Date date) {
        //根据天统计
        FimResultAnalysisDTO analysisDTO = new FimResultAnalysisDTO();
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        //报警总数
        Integer total = fimAlgResultMapper.countResultTotal(Arrays.asList(deptId), startTime, endTime);
        analysisDTO.setTotal(total);
        //统计每种报警类型的报警总数
        List<FimAlgCheckTypeCountDTO> algCheckTypeCountList = fimAlgResultMapper.selectTagAmount(deptId, startTime, endTime);
        //取出已有的报警类型
        List<Integer> checkTypeList = algCheckTypeCountList.stream()
                .map(FimAlgCheckTypeCountDTO::getAlgCheckType)
                .collect(Collectors.toList());
        //加入报警记录等于0的数据
        List<FimAlgCheckTypeDTO> algCheckTypeList = fimWarnRuleMapper.selectAlgCheckTypeByDeptId(deptId);
        for (FimAlgCheckTypeDTO fimAlgCheckTypeDTO : algCheckTypeList) {
            if (!checkTypeList.contains(fimAlgCheckTypeDTO.getAlgCheckType())) {
                FimAlgCheckTypeCountDTO checkTypeCountDTO = new FimAlgCheckTypeCountDTO();
                checkTypeCountDTO.setAlgCheckType(fimAlgCheckTypeDTO.getAlgCheckType());
                checkTypeCountDTO.setAlgCheckTypeName(fimAlgCheckTypeDTO.getAlgCheckTypeName());
                checkTypeCountDTO.setCount(0);
                algCheckTypeCountList.add(checkTypeCountDTO);
            }
        }
        analysisDTO.setAlgCheckTypeCountList(algCheckTypeCountList);
        return analysisDTO;
    }

    @Override
    public Integer getResultTotal(List<Integer> deptIds, Date startTime, Date endTime) {
        return fimAlgResultMapper.countResultTotal(deptIds, startTime, endTime);
    }

    @Override
    public List<FimResultRateContDTO> warnNumstatic(Integer deptId, Integer handleStatus, Date startTime, Date endTime) {
        List<FimResultRateContDTO> list = fimAlgResultMapper.selectWarnNum(deptId, handleStatus, startTime, endTime);
        List<FimResultRateContDTO> fimResultRateContDTOS = this.staticGeneralWarnNum(deptId, list);
        return fimResultRateContDTOS;
    }

    @Override
    public FimWarnAnalysisDTO analysisByHour(Integer deptId, Date startTime, Date endTime) {
        List<FimResultRateContDTO> list = fimAlgResultMapper.selectTagFrequency(deptId, startTime, endTime, HOUR_TIME_SQL_FORMAT);
        List<Date> hourList = DateUtil.getHourListBetween(startTime, endTime);

        // 统计报警的总体数据
        List<FimResultRateContDTO> staticDTO = this.staticGeneralWarnNum(deptId, list);

        // 按小时统计报警数据
        List<FimWarnStaticDTO> dataList = this.staticNumByTime(deptId, hourList, list, HOUR_TIME_FORMAT);

        FimWarnAnalysisDTO result = new FimWarnAnalysisDTO();
        result.setDataList(dataList);
        result.setFimWarnStatic(staticDTO);
        return result;
    }

    @Override
    public FimWarnAnalysisDTO analysisByDate(Integer deptId, Date startDate, Date endDate) {
        Date startTime = DateUtil.getDateBegin(startDate);
        Date endTime = DateUtil.getDateEnd(endDate);
        List<FimResultRateContDTO> list = fimAlgResultMapper.selectTagFrequency(deptId, startTime, endTime, DAY_TIME_SQL_FORMAT);
        List<Date> dateList = DateUtil.getDateListBetween(startDate, endDate);

        // 统计报警的总体数据
        List<FimResultRateContDTO> staticDTO = this.staticGeneralWarnNum(deptId, list);

        // 按天统计报警数据
        List<FimWarnStaticDTO> dataList = this.staticNumByTime(deptId, dateList, list, DAY_TIME_FORMAT);

        FimWarnAnalysisDTO result = new FimWarnAnalysisDTO();
        result.setFimWarnStatic(staticDTO);
        result.setDataList(dataList);
        return result;
    }

    @Override
    public FimWarnNumDTO getWarnNum(Integer deptId, Integer algCheckType, Date startTime, Date endTime) {
        List<FimResultDTO> fimResultDTOS = fimAlgResultMapper.selectFimResultList(deptId, null, algCheckType, startTime, endTime);
        long total = fimResultDTOS.size();
        long handleNum = fimResultDTOS.stream().filter(obj -> obj.getHandleStatus().equals(HandleFlag.HANDLE.getValue())).count();
        long unHandleNum = fimResultDTOS.stream().filter(obj -> obj.getHandleStatus().equals(HandleFlag.UNHANDLE.getValue())).count();
        FimWarnNumDTO data = new FimWarnNumDTO();
        data.setTotal(total);
        data.setHandleNum(handleNum);
        data.setUnHandleNum(unHandleNum);
        return data;
    }

    /**
     * 获取所有的检测类型
     *
     * @param deptId
     * @return
     */
    private List<FimResultRateContDTO> getAllCheckType(Integer deptId) {
        List<FimResultRateContDTO> fimAlgCheckTypeDTOS = fimWarnRuleMapper.selectCheckTypeByDeptId(deptId);
        return fimAlgCheckTypeDTOS;
    }

    /**
     * 统计报警的总体数据
     *
     * @param deptId
     * @param list
     * @return
     */
    private List<FimResultRateContDTO> staticGeneralWarnNum(Integer deptId, List<FimResultRateContDTO> list) {

        /*
        List<FimResultRateContDTO> allCheckType = this.getAllCheckType(deptId);
        for (FimResultRateContDTO data : allCheckType) {
            Integer algCheckType = data.getAlgCheckType();
            int count = 0;
            for (FimResultRateContDTO resultRateContDTO : list) {
                if (algCheckType.equals(resultRateContDTO.getAlgCheckType())) {
                    count += resultRateContDTO.getCount();
                }
            }
            data.setCount(count);
        }
        return allCheckType;
         */

        List<FimResultRateContDTO> resList = this.getAllCheckType(deptId);
        Map<Integer, List<Integer>> map = list.stream().collect(Collectors.groupingBy(FimResultRateContDTO::getAlgCheckType, Collectors.mapping(FimResultRateContDTO::getCount, Collectors.toList())));

        for (FimResultRateContDTO v : resList) {
            int count = 0;
            List<Integer> countList = map.get(v.getAlgCheckType());
            if (!CollectionUtils.isEmpty(countList)) {
                count = countList.stream().mapToInt(Integer::intValue).sum();
            }
            v.setCount(count);
        }
        return resList;
    }

    /**
     * 按时间统计报警数据
     *
     * @param deptId
     * @param timeList
     * @param list
     * @param timeFormat
     * @return
     */
    private List<FimWarnStaticDTO> staticNumByTime(Integer deptId, List<Date> timeList, List<FimResultRateContDTO> list, String timeFormat) {
        List<FimResultRateContDTO> allCheckType = this.getAllCheckType(deptId);
        List<FimWarnStaticDTO> dataList = new ArrayList<>();
        for (Date date : timeList) {
            String dateStr = DateUtil.formatDate(date, timeFormat);
            List<FimResultRateContDTO> checkTypeList = this.copyList(allCheckType);
            for (FimResultRateContDTO checkType : checkTypeList) {
                for (FimResultRateContDTO resultRateContDTO : list) {
                    if (resultRateContDTO.getTimeStr().equals(dateStr) && resultRateContDTO.getAlgCheckType().equals(checkType.getAlgCheckType())) {
                        checkType.setCount(resultRateContDTO.getCount());
                    }
                }
            }
            FimWarnStaticDTO item = new FimWarnStaticDTO();
            item.setTime(dateStr);
            item.setFimWarnStatic(checkTypeList);
            item.setTotal(checkTypeList.stream().mapToInt(obj -> obj.getCount()).sum());
            dataList.add(item);
        }
        return dataList;
    }

    /**
     * 复制集合
     *
     * @param allCheckType
     * @return
     */
    private List<FimResultRateContDTO> copyList(List<FimResultRateContDTO> allCheckType) {
        List<FimResultRateContDTO> list = new ArrayList();
        for (FimResultRateContDTO resultRateContDTO : allCheckType) {
            FimResultRateContDTO data = new FimResultRateContDTO();
            BeanUtils.copyProperties(resultRateContDTO, data);
            list.add(data);
        }
        return list;
    }

    @Override
    public FimWarnStatisticsDetailDTO getFimWarnStatisticsDetail(FimSearchParam param) {

        List<FimResultRateContDTO> allDataList = this.getAllCheckType(param.getDeptId());

        param.setHandleStatus(HandleFlag.HANDLE.getValue());
        List<FimResultRateContDTO> handWarnList = handleWarnData(allDataList, fimAlgResultMapper.selectStatisticsDetail(param));

        param.setHandleStatus(HandleFlag.UNHANDLE.getValue());
        List<FimResultRateContDTO> unHandWarnList = handleWarnData(allDataList, fimAlgResultMapper.selectStatisticsDetail(param));

        FimWarnStatisticsDetailDTO res = new FimWarnStatisticsDetailDTO();
        res.setHandleWarnList(handWarnList);
        res.setHandleCount(handWarnList.stream().mapToInt(FimResultRateContDTO::getCount).sum());
        res.setUnHandleWarnList(unHandWarnList);
        res.setUnHandleCount(unHandWarnList.stream().mapToInt(FimResultRateContDTO::getCount).sum());
        return res;
    }


    private List<FimResultRateContDTO> handleWarnData(List<FimResultRateContDTO> allDataList, List<FimResultRateContDTO> actualList) {

        List<FimResultRateContDTO> resList = new ArrayList<>();
        Map<Integer, Integer> map = new HashMap<>();

        actualList.forEach(m -> map.put(m.getAlgCheckType(), m.getCount()));

        for (FimResultRateContDTO v : allDataList) {
            FimResultRateContDTO data = new FimResultRateContDTO();
            BeanUtils.copyProperties(v, data);
            data.setCount(map.getOrDefault(v.getAlgCheckType(), 0));
            resList.add(data);
        }
        return resList;
    }

}
